#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <sys/wait.h>
#include <unistd.h>
#include "Task.hpp"

class Channel                       // 担任管道操作的对象
{                                   // 1.唤醒子进程
public:                             // 2.回收子进程
                                    // 3.关闭父进程写端 wfd
    Channel(int wfd, pid_t subPid)
        :_wfd(wfd)
        ,_subPid(subPid)
        ,_name("channel-" + std::to_string(wfd) + "-" + std::to_string(subPid))
    {
        ;
    }

    void send(int code)             // 对管道写入唤醒子进程
    {
        int n = write(_wfd, &code, sizeof(code));
    }

    void subWait()                  // 回收子进程
    {
        pid_t pid = waitpid(_subPid, nullptr, 0);
    }

    void fdClose()                  // 关闭父进程的wfd
    {
        close(_wfd);
    }

    int getFd()
    {
        return _wfd;
    }

    pid_t getSubPide()
    {
        return _subPid;
    }

    std::string getName()
    {
        return _name;
    }

    ~Channel()
    {
        ;
    }

private:

    int _wfd;
    pid_t _subPid;
    std::string _name;
};

class ChannelManager                            // 管理 Channel
{
public:

    ChannelManager()
        :_next(0)
    {
        ;
    }

    void insert(const Channel& channel)         // 存储父进程管道 wfd 与子进程 pid
    {
        _channels.push_back(channel);
    }

    template<class ...Args>
    void emplace(Args&&... args)            
    {
        _channels.emplace_back(args...);
    }

    Channel& select()
    {
        Channel& channel = _channels[_next];
        _next = (_next + 1) % _channels.size();
        return channel;
    }

    void printSubProcess()
    {
        for (auto& channel : _channels)
        {
            std::cout << channel.getName() << std::endl;
        }
    }

    void waitSubProcess()
    {
        for (auto& channel : _channels)
        {
            std::cout << "回收子进程:" << channel.getName() << std::endl;
            channel.subWait();
        }
    }

    void stopSubProcess()
    {
        for (auto& channel : _channels)
        {
            std::cout << "关闭写端:" << channel.getFd() << std::endl;
            channel.fdClose();
        }
    }

    void closeAndWait()
    {
        // bug: 父进程 fork 时会将之前存储哥哥的写端 wfd 一并继承给当前子进程，
        // 导致前面的匿名管道引用计数随之后的子进程增加

        // 方法1: 倒着关闭写端和回收子进程即可

        // for (int i = _channels.size() - 1; i >= 0; --i)
        // {
        //     _channels[i].fdClose();
        //     _channels[i].subWait();
        // }

        // 方案2: 在 fork 子进程时，子进程的 _cm 获取的是父进程之前哥哥进程的写端
        // 直接在子进程执行任务之前将所有的写端关闭即可
        // 更推荐这种方式

        for (auto& channel : _channels)
        {
            channel.fdClose();
            channel.subWait();
        }
    }

    ~ChannelManager()
    {
        ;
    }

private:

    std::vector<Channel> _channels;
    size_t _next;
};

class ProcessPool
{
public:

    ProcessPool(int num = 5)
        :_process_num(num)
    {
        _tm.registered(printLog);
        _tm.registered(download);
        _tm.registered(upload);
    }

    void work(int rfd)                          // 子进程进入死循环，在读取函数 read 读取管道数据时会处于阻塞状态, 一般有两种情况
    {                                           // 1.当 Channel 使用 write 在管道写入信息时，子进程会读取任务码，执行其任务，退出后继续循环
        while (true)                            // 2.当父进程写端关闭时，子进程读取的 read 不再是阻塞状态，其返回 0 ,随后退出循环
        {
            int code = 0;
            
            int n = read(rfd, &code, sizeof(code));

            if (n > 0)
            {
                if (n != sizeof(code))          // 读取字节不完全不执行
                {
                    continue;
                }

                std::cout << "子进程[" << getpid() << "]收到一个任务码: " << code << std::endl;
                _tm.execute(code);                    
            }
            else if (n == 0)
            {
                std::cout << "子进程退出" << std::endl;
                break;
            }
            else
            {
                std::cout << "读取错误" << std::endl;
                break;
            }
        }
    }

    void run()
    {
        int taskCode = _tm.code();              // 获取任务码(任务在 _tm 所处的下标)

        auto& channel = _cm.select();           // 选择子进程(使用的是轮询的方法)  

        channel.send(taskCode);                 // 发送任务(唤醒子进程)
    }

    void debug()
    {
        _cm.printSubProcess();
    }

    void stop()
    {
        _cm.closeAndWait();
        // _cm.stopSubProcess();
        // _cm.waitSubProcess();
    }

    bool create()
    {
        for (int i = 0; i < _process_num; ++i)
        {
            int pfd[2] = { 0 };
            int n = pipe(pfd);
            if (n != 0)
            {
                return false;
            }

            int subPid = fork();

            if (subPid < 0)
            {
                return false;
            }
            else if (subPid == 0)
            {
                _cm.stopSubProcess();
                std::cout << "------------" << std::endl;
                
                close(pfd[1]);                  // 关闭子进程写端
                work(pfd[0]);                   // 执行任务
                close(pfd[0]);                  // 关闭读端(算是冗余设计)
                exit(0);                        // 子进程退出
            }
            else
            {
                close(pfd[0]);                  // 关闭父进程读端
                
                _cm.emplace(pfd[1], subPid);    // 记录父进程匿名管道写端和子进程

                // close(pfd[1]);
            }

            sleep(1);                           // 测试 
        }
    }

    ~ProcessPool()
    {
        ;
    }

private:

    ChannelManager _cm;                         // 管理子进程
    size_t _process_num;
    TaskManager _tm;                            // 任务派发管理

};